#!/usr/bin/python
# -*- coding: utf-8 -*-
# Project : flacon
# File : flacon.py
# (c) Alexander Sokolov sokoloff.a@gmail.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.


FLACON_VERSION='0.8.0'

import sys
import os
import string

from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import *
from mainWindow_ui import Ui_mainWindow


from converter import *
from settings import *
from configureDialog import *
from flaconglobal import *
from disc import *
from trackview import *
from audiofiles import *
from track import *
from trackset import *
from project import *

import pyqticonloader
import aboutDialog
import errors
import traceback

class Flacon(QMainWindow, Ui_mainWindow):
   #****************************************************************
   #
   #****************************************************************
    def __init__(self):
        QMainWindow.__init__(self)
        self.setupUi(self)

        self.setWindowIcon(pyqticonloader.icon('flacon'))
        self.setAcceptDrops(True)

        self._project = Project()
        self.converter = Converter(self)
        self._scannedNow = False

        # TrackView ...............................................
        self.trackView.setProject(self._project)

        self.trackView.setRootIsDecorated(False)
        self.trackView.setItemsExpandable(False)
        self.trackView.hideColumn(TrackView.COLUMN_COMMENT)


        self.initActions()

        # Buttons ..................................................
        self.outDirButton.setDefaultAction(self.actionSelectResultDir)
        self.configureEncoderBtn.setDefaultAction(self.actionConfigureEncoder)
        self.initOutPatternButton()

        # Combo ....................................................
        self.initOutFormatCombo()

        self.loadSettings()

        # Signals ..................................................
        self.connect(settings, SIGNAL('Changed()'), self.trackView.model(), SIGNAL('layoutChanged()'))

        self.connect(self.outPatternEdit, SIGNAL('editingFinished()'), self.setPattern)
        self.connect(self.outDirEdit,     SIGNAL('editingFinished()'), self.setOutDir)

        self.connect(self.outFormatCombo, SIGNAL('currentIndexChanged(int)'), self.setOutFormat)
        self.connect(self.codepageCombo, SIGNAL('currentIndexChanged(int)'), self.setCodePage)

        self.connect(self.tagGenreEdit,    SIGNAL('editingFinished()'),   (lambda: self.setTag('GENRE')))
        self.connect(self.tagYearEdit,     SIGNAL('editingFinished()'),   (lambda: self.setTag('YEAR')))
        self.connect(self.tagArtistEdit,   SIGNAL('editingFinished()'),   (lambda: self.setTag('PERFORMER')))
        self.connect(self.tagAlbumEdit,    SIGNAL('editingFinished()'),   (lambda: self.setTag('ALBUM')))
        self.connect(self.tagDiscIdEdit,   SIGNAL('editingFinished()'),   (lambda: self.setTag('DISCID')))
        self.connect(self.tagStartNumEdit, SIGNAL('editingFinished()'),     self.setStartTrackNum)

        self.connect(self.converter, SIGNAL('finished()'), self.setControlsEnable)
        self.connect(self.converter, SIGNAL('error(QString)'), self.showErrorMessage)
        self.connect(self.converter, SIGNAL('progressChanged(int,int)'), self.trackView.model().progressChanged)

        self.connect(self.trackView, SIGNAL('selectCueFile(int)'), self.setCueForDisc)
        self.connect(self.trackView, SIGNAL('selectAudioFile(int)'), self.setAudioForDisc)

        self.connect(self.trackView.model(), SIGNAL('layoutChanged()'), self.refreshEdits)
        self.connect(self.trackView.model(), SIGNAL('layoutChanged()'), self.setControlsEnable)

        self.connect(self.trackView.selectionModel(), SIGNAL('selectionChanged(QItemSelection,QItemSelection)'), self.refreshEdits)
        self.connect(self.trackView.selectionModel(), SIGNAL('selectionChanged(QItemSelection,QItemSelection)'), self.setControlsEnable)

        self.connect(self._project, SIGNAL('layoutChanged()'), self.refreshEdits)
        self.connect(self._project, SIGNAL('layoutChanged()'), self.setControlsEnable)

        self.connect(self, SIGNAL("showErrorMessage(QString)"), self.showErrorMessage)

        self.refreshEdits()
        self.setControlsEnable()


   #****************************************************************
   #
   #****************************************************************
    def initActions(self):
        getIcon = pyqticonloader.icon

        self.actionAddFile.setIcon(getIcon(['document-open', 'fileopen']))
        self.connect(self.actionAddFile, SIGNAL('triggered()'), self.openAddFileDialog)

        self.actionRemoveDisc.setIcon(getIcon(['edit-delete', 'remove']))
        self.connect(self.actionRemoveDisc, SIGNAL('triggered()'), self.removeDisc)

        self.actionScan.setIcon(getIcon(['document-open-folder', 'document-open', 'folder_open']))
        self.connect(self.actionScan, SIGNAL('triggered()'), self.openScanDialog)

        self.actionDownloadTrackInfo.setIcon(getIcon(['download', 'web-browser', 'network']))
        self.connect(self.actionDownloadTrackInfo, SIGNAL('triggered()'), self.downloadInfo)


        self.actionStartConvert.setIcon(getIcon(['dialog-ok', 'button_ok']))
        self.connect(self.actionStartConvert, SIGNAL('triggered()'), self.startConvert)

        self.actionAbortConvert.setIcon(getIcon(['dialog-cancel', 'button_cancel']))
        self.connect(self.actionAbortConvert, SIGNAL('triggered()'), self.abortConvert)

        self.actionSelectResultDir.setIcon(getIcon(['document-open-folder', 'document-open', 'folder_open']))
        self.connect(self.actionSelectResultDir, SIGNAL('triggered()'), self.openOutDirDialog)

        self.actionConfigure.setIcon(getIcon(['configure', 'preferences-system']))
        self.connect(self.actionConfigure, SIGNAL('triggered()'), self.configure)

        self.actionConfigureEncoder.setIcon(self.actionConfigure.icon())
        self.connect(self.actionConfigureEncoder, SIGNAL('triggered()'), self.configureEncoder)

        self.actionAbout.setIcon(getIcon(['help-about', 'info']))
        self.connect(self.actionAbout, SIGNAL('triggered()'), self.aboutFlacon)


   #****************************************************************
   #
   #****************************************************************
    def initOutFormatCombo(self):
        def nameCmp((xf, xi), (yf, yi)):
            return cmp(xi.name, yi.name)

        formats = outputAudioFormats.formats.iteritems()
        for format, info in sorted(formats, cmp=nameCmp):
            self.outFormatCombo.addItem(info.name, QVariant(format))


   #****************************************************************
   #
   #****************************************************************
    def insertOutPattern(self, pattern):
        self.outPatternEdit.insert(pattern);
        self.setPattern()


   #****************************************************************
   #
   #****************************************************************
    def initOutPatternButton(self):
        def addAction(menu, pattern, title):
            act = QtGui.QAction(title, self)
            self.connect(act, SIGNAL('triggered()'), lambda: self.insertOutPattern(pattern))
            menu.addAction(act)


        menu = QMenu(self.outPatternButton)

        addAction(menu, '%n', self.tr('Insert "Track number"'))
        addAction(menu, '%N', self.tr('Insert "Total number of tracks"'))
        addAction(menu, '%a', self.tr('Insert "Artist"'))
        addAction(menu, '%A', self.tr('Insert "Album title"'))
        addAction(menu, '%t', self.tr('Insert "Track title"'))
        addAction(menu, '%y', self.tr('Insert "Year"'))
        addAction(menu, '%g', self.tr('Insert "Genre"'))

        self.outPatternButton.setPopupMode(QToolButton.InstantPopup)
        self.outPatternButton.setMenu(menu)


   #****************************************************************
   #
   #****************************************************************
    def closeEvent(self, event):
        self.converter.stop()
        self.saveSettings()


   #****************************************************************
   #
   #****************************************************************
    def keyPressEvent(self, event ):
        if event.key() == Qt.Key_Escape:
            self._scannedNow = False


   #****************************************************************
   # Load settings
   #****************************************************************
    def loadSettings(self):
        # MainWindow geometry
        (width, ok)  = settings.value("MainWindow/Width",  QVariant(987)).toInt()
        (height, ok) = settings.value("MainWindow/Height", QVariant(450)).toInt()
        self.resize(width, height)

        self.splitter.restoreState(settings.value("MainWindow/Splitter").toByteArray())
        self.trackView.header().restoreState(settings.value("MainWindow/TrackView").toByteArray())


   #****************************************************************
   # Write settings
   #****************************************************************
    def saveSettings(self):
        settings.setValue("MainWindow/Width",     QVariant(self.size().width()))
        settings.setValue("MainWindow/Height",    QVariant(self.size().height()))
        settings.setValue("MainWindow/Splitter",  QVariant(self.splitter.saveState()))
        settings.setValue("MainWindow/TrackView", QVariant(self.trackView.header().saveState()))


   #****************************************************************
   #
   #****************************************************************
    def removeDisc(self):
        discs = self.trackView.selectedDiscs()
        for disc in discs:
            self._project.removeDisc(disc)

        selModel = self.trackView.selectionModel()
        selModel.select(selModel.currentIndex(), QItemSelectionModel.Select)
        self.trackView.scrollTo(selModel.currentIndex(), QAbstractItemView.PositionAtCenter)


   #****************************************************************
   #
   #****************************************************************
    def setCueForDisc(self, index):
        disc = self._project[index]
        flt = self.getOpenFileFilter(False, True)

        if not disc.audioFileName().isEmpty():
            dir = QFileInfo(disc.audioFileName()).dir().absolutePath()
        elif not disc.cueFileName().isEmpty():
            dir = QFileInfo(disc.cueFileName()).dir().absolutePath()
        else:
            dir = settings.value("Misc/LastDirectory").toString()


        fileName = QFileDialog.getOpenFileName(self, self.tr("Select CUE file", "OpenFile dialog title"), dir, flt)

        if fileName:
            tracks = CueTrackSet(fileName)

            for t in disc.trackSets():
                if isinstance(t, CueTrackSet):
                    disc.trackSets().remove(t)

            disc.addTrackSet(tracks)
            tracks.activate()


   #****************************************************************
   #
   #****************************************************************
    def setAudioForDisc(self, index):
        disc = self._project[index]
        flt = self.getOpenFileFilter(True, False)

        if not disc.cueFileName().isEmpty():
            dir = QFileInfo(disc.cueFileName()).dir().absolutePath()
        elif not disc.audioFileName().isEmpty():
            dir = QFileInfo(disc.audioFileName()).dir().absolutePath()
        else:
            dir = settings.value("Misc/LastDirectory").toString()


        fileName = QFileDialog.getOpenFileName(self, self.tr("Select audio file", "OpenFile dialog title"), dir, flt)

        if fileName:
            audio = AudioFile(fileName)
            disc.setAudioFile(audio)


   #****************************************************************
   #
   #****************************************************************
    def _fixFileNameBug(self, fileName):
        if not QFileInfo(fileName).exists():
            fileName = fileName.replace("%20", " ")
        return fileName


   #****************************************************************
   #
   #****************************************************************
    def getOpenFileFilter(self, includeAudio, includeCue):
        flt = QStringList()
        allFlt = QStringList()

        if includeAudio:
            for format, info in InputAudioFormats.formats.iteritems():
                allFlt << QString(" *.%1").arg(info.ext)
                flt << self.tr("%1 files", 'OpenFile dialog filter line, like "WAV files"').arg(info.name) + " (*." + info.ext + ")"

        flt.sort()

        if includeCue:
            allFlt << QString("*.cue")
            flt.insert(0, self.tr("%1 files", 'OpenFile dialog filter line, like "WAV files"').arg("CUE") + " (*.cue)")

        if len(allFlt) > 1:
            flt.insert(0, self.tr("All supported formats", "OpenFile dialog filter line") + " (" + allFlt.join(" ")  + ")")

        flt << self.tr("All files", 'OpenFile dialog filter line like "All files"') + " (*)"

        return flt.join(";;")



   #****************************************************************
   #
   #****************************************************************
    def openAddFileDialog(self):
        flt = self.getOpenFileFilter(True, True)

        lastDir = settings.value("Misc/LastDirectory").toString()
        fileNames = QFileDialog.getOpenFileNames(self, self.tr("Add CUE or audio file", "OpenFile dialog title"), lastDir, flt)

        for fileName in fileNames:
            fileName = self._fixFileNameBug(fileName)
            settings.setValue("Misc/LastDirectory", QFileInfo(fileName).dir().path())
            self.addFileOrDir(fileName)


   #****************************************************************
   #
   #****************************************************************
    def addFileOrDir(self, fileName):
        fi = QFileInfo(fileName)
        if fi.isDir():              self.scan(fi.canonicalFilePath())
        elif fi.size() > 102400:    self.addAudioFile(fi.canonicalFilePath())
        else:                       self.addCueFile(fi.canonicalFilePath())


   #****************************************************************
   #
   #****************************************************************
    def addCueFile(self, cueFileName):
        canonicalFileName = QFileInfo(cueFileName).canonicalFilePath()

        for disc in self._project:
            if disc.cueFileName() == canonicalFileName:
                return

        disc = Disc()

        tracks = CueTrackSet(cueFileName)
        disc.addTrackSet(tracks)
        tracks.activate()

        audio = self.findAudioFile(cueFileName, tracks.fileTag())
        if audio:
            disc.setAudioFile(audio)

        self._project.insertDisc(disc)


   #****************************************************************
   #
   #****************************************************************
    def addAudioFile(self, audioFileName):
        audioFileName = QFileInfo(audioFileName).canonicalFilePath()

        for disc in self._project:
            if disc.audioFileName() == audioFileName:
                return

        disc = Disc()
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            audio = AudioFile(audioFileName)
            disc.setAudioFile(audio)
            tracks = self.findCueFile(audioFileName)
            if tracks:
                disc.addTrackSet(tracks)
                tracks.activate()

            self._project.insertDisc(disc)

        finally:
            QApplication.restoreOverrideCursor()


   #****************************************************************
   #
   #****************************************************************
    def findAudioFile(self, cueFileName, fileTag):
        exts = QStringList()
        for format, info in InputAudioFormats.formats.iteritems():
            exts << "*." + info.ext

        fi = QFileInfo(cueFileName)

        patterns = QStringList()
        patterns << fi.completeBaseName()

        if not fileTag.isEmpty():
            patterns << QFileInfo(fileTag).completeBaseName()

        files = fi.dir().entryInfoList(exts, QDir.Files | QDir.Readable)
        for f in files:
            for pattern in patterns:
                if f.fileName().startsWith(pattern):
                    try:
                        audio = AudioFile(f.absoluteFilePath())
                        return audio
                    except FileError, e:
                        pass

        return None


   #****************************************************************
   #
   #****************************************************************
    def findCueFile(self, audioFileName):
        exts = QStringList()
        exts << "*.cue"

        fi = QFileInfo(audioFileName)
        pattern = fi.completeBaseName()

        files = fi.dir().entryInfoList(exts, QDir.Files | QDir.Readable)

        for f in files:
            if f.fileName().startsWith(pattern):
                try:
                    tracks = CueTrackSet(f.absoluteFilePath())
                    return tracks
                except FileError, e:
                    pass

        return None


   #****************************************************************
   #
   #****************************************************************
    def openScanDialog(self):
        lastDir = settings.value("Misc/LastDirectory").toString()
        dir = QFileDialog.getExistingDirectory(self, self.tr("Select directory"), lastDir)
        if (dir):
            settings.setValue("Misc/LastDirectory", dir)
            self.scan(dir)


   #****************************************************************
   #
   #****************************************************************
    def scan(self, dirName):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        self._scannedNow = True

        exts = QStringList()
        for format, info in InputAudioFormats.formats.iteritems():
            exts << "*." + info.ext

        query = set([dirName])

        try:
            while len(query):
                qd = QDir(query.pop())

                dirs = qd.entryInfoList(QDir.Dirs | QDir.Readable | QDir.NoDotAndDotDot) #| QDir.NoSymLinks
                for d in dirs:
                    qApp.processEvents()
                    if not self._scannedNow:
                        raise errors.Abort()
                    query.add(d.absoluteFilePath())

                files = qd.entryInfoList(exts, QDir.Files | QDir.Readable)
                for f in files:
                    qApp.processEvents()
                    if not self._scannedNow:
                        raise errors.Abort()

                    try:
                        self.addAudioFile(f.absoluteFilePath())
                    except FileError:
                        pass
        except Abort:
            pass

        self._scannedNow = False

        QApplication.restoreOverrideCursor()
        self.setControlsEnable()


   #****************************************************************
   #
   #****************************************************************
    def downloadInfo(self):
        for disc in self.trackView.selectedDiscs():
            disc.downloadInfo()


   #****************************************************************
   # Set tag for all selected tracks
   #****************************************************************
    def setTag(self, tagName):
        edit = self.sender()

        if not edit.isModified():
            return

        value = edit.text()

        tracks = self.trackView.selectedTracks()
        for track in tracks:
            track.setTag(tagName, value)


   #****************************************************************
   #
   #****************************************************************
    def setStartTrackNum(self):
        spinBox = self.sender()
        if not  spinBox.isModified():
            return

        value = spinBox.value()

        discs = self.trackView.selectedDiscs()
        for disc in discs:
            disc.tracks().setStartTrackNum(value)


   #****************************************************************
   #
   #****************************************************************
    def setCodePage(self, index):
        if index < 0:
            return

        codePage = self.codepageCombo.codePage()
        discs = self.trackView.selectedDiscs()
        for disc in discs:
            disc.tracks().setCodePage(codePage)

        self.refreshEdits()


   #****************************************************************
   #
   #****************************************************************
    def setOutFormat(self, index):
        format = self.outFormatCombo.itemData(index)
        settings.setValue('OutFiles/Format', format)


   #****************************************************************
   #
   #****************************************************************
    def setPattern(self):
        settings.setValue('OutFiles/Pattern', self.outPatternEdit.text())


   #****************************************************************
   #
   #****************************************************************
    def openOutDirDialog(self):
        outDir = QFileDialog.getExistingDirectory(self, self.tr("Select result directory"), self.outDirEdit.text())
        if (outDir):
            self.outDirEdit.setText(outDir)
            self.setOutDir()


   #****************************************************************
   #
   #****************************************************************
    def setOutDir(self):
        settings.setValue('OutFiles/Directory', self.outDirEdit.text())


   #****************************************************************
   #
   #****************************************************************
    def refreshEdits(self):
        # Track tags :::::::::::::::::::::::::::::::::::::::::::::::
        year = []
        artist = []
        album = []
        genre = []

        tracks = self.trackView.selectedTracks()
        for track in tracks:
            year.append(track.year())
            artist.append(track.artist())
            album.append(track.album())
            genre.append(track.genre())

        self.tagYearEdit.setValue(year)
        self.tagArtistEdit.setText(artist)
        self.tagAlbumEdit.setText(album)
        self.tagGenreEdit.setText(genre)


        # Disc tags ::::::::::::::::::::::::::::::::::::::::::::::::
        startNum = []
        codePage = []
        discId = []

        discs = self.trackView.selectedDiscs()
        for disc in discs:
            tracks = disc.tracks()
            startNum.append(tracks.startTrackNum())
            codePage.append(tracks.codePage())
            discId.append(tracks.discId())

        if not codePage:
            codePage = settings.value("Tags/DefaultCodepage").toString().toLatin1()
            
        self.tagStartNumEdit.setValue(startNum)
        self.codepageCombo.setCodePage(codePage)
        self.tagDiscIdEdit.setText(discId)

        # Global tags ::::::::::::::::::::::::::::::::::::::::::::::
        format = settings.value("OutFiles/Format")
        n = self.outFormatCombo.findData(format)
        self.outFormatCombo.setCurrentIndex(n)

        outDir = settings.value('OutFiles/Directory').toString()
        if (self.outDirEdit.text() != outDir):
            self.outDirEdit.setText(outDir)

        s = settings.value("OutFiles/Pattern").toString()
        if self.outPatternEdit.text() != s:
            self.outPatternEdit.setText(s)


   #****************************************************************
   #
   #****************************************************************
    def startConvert(self):
        self.trackView.setFocus()

        err = 0
        for disc in self._project:
            if not disc.canConvert():
                err += 1
                break


        if err:
            res = QMessageBox.warning(self,
                    self.windowTitle(),
                    self.tr("Some albums will not be converted, they contain errors.\nDo you want to continue?"),
                    QMessageBox.Ok | QMessageBox.Cancel
                )

            if res !=  QMessageBox.Ok:
                return


        for disc in self._project:
            for track in disc.tracks():
                track.setProgress(Statuses.NotRunning, -1)

        self.trackView.setColumnWidth(0, 200)
        self.converter.start(self._project)
        self.setControlsEnable()


   #****************************************************************
   #
   #****************************************************************
    def abortConvert(self):
        self.converter.stop()
        self.setControlsEnable()


   #****************************************************************
   #
   #****************************************************************
    def setControlsEnable(self):
        running = self.converter.isRunning()

        if running:
            self.outFilesBox.setEnabled(False)
            self.tagsBox.setEnabled(False)

            self.actionAddFile.setEnabled(False)
            self.actionRemoveDisc.setEnabled(False)
            self.actionStartConvert.setEnabled(False)
            self.actionAbortConvert.setEnabled(True)
            self.actionDownloadTrackInfo.setEnabled(False)
            self.actionScan.setEnabled(False)
            self.actionConfigure.setEnabled(False)
            self.actionConfigureEncoder.setEnabled(False)

        else:
            tracksSelected = len(self.trackView.selectedTracks()) > 0
            discsSelected  = len(self.trackView.selectedDiscs()) > 0
            canConvert = self._project.canConvert()

            self.outFilesBox.setEnabled(True)
            self.tagsBox.setEnabled(tracksSelected)

            self.actionAddFile.setEnabled(True)
            self.actionRemoveDisc.setEnabled(discsSelected)
            self.actionStartConvert.setEnabled(canConvert)
            self.actionAbortConvert.setEnabled(running)
            self.actionDownloadTrackInfo.setEnabled(tracksSelected)
            self.actionScan.setEnabled(not self._scannedNow)
            self.actionConfigure.setEnabled(True)
            self.actionConfigureEncoder.setEnabled(outputAudioFormats.current().configPageClass != None)


   #****************************************************************
   #
   #****************************************************************
    def aboutFlacon(self):
        about = aboutDialog.AboutDialog(self)
        about.exec_()


   #****************************************************************
   #
   #****************************************************************
    def configure(self):
        ConfigureDialog.createAndShow(self)


   #****************************************************************
   #
   #****************************************************************
    def configureEncoder(self):
        fmt = outputAudioFormats.current()
        if fmt.configPageClass != None:
            ConfigureDialog.createAndShow(self, fmt.id)


   #****************************************************************
   #
   #****************************************************************
    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
             event.acceptProposedAction()


   #****************************************************************
   #
   #****************************************************************
    def dropEvent(self, event):
        for url in event.mimeData().urls():
            self.addFileOrDir(url.path())


   #****************************************************************
   #
   #****************************************************************
    def showErrorMessage(self, msg):
        QMessageBox.critical(self, QCoreApplication.translate("Flacon", "Error"), QString(msg))


   #****************************************************************
   #
   #****************************************************************
    def exceptHook(self, exType, value, tracebak):
        if exType == exceptions.KeyboardInterrupt:
            exit()

        QApplication.restoreOverrideCursor()
        if not isinstance(value, errors.FlaconException):
            traceback.print_exception(exType, value, tracebak)

        self.emit(SIGNAL("showErrorMessage(QString)"), QString(unicode(value)))


#*******************************************************************
#
#*******************************************************************
def localeName():
    try:
        return QLocale(os.environ['LANG']).name()
    except KeyError:
        return QLocale.system().name()


#*******************************************************************
#
#*******************************************************************
def translationFileName():
    dir = QDir(modulesDir() + "/translations")

    locale = localeName()
    
    if dir.exists(QString("flacon_%1.qm").arg(locale)):
        return dir.absoluteFilePath(QString("flacon_%1.qm").arg(locale))
    
    lang = locale.left(2)

    if dir.exists(QString("flacon_%1.qm").arg(lang)):
        return dir.absoluteFilePath(QString("flacon_%1.qm").arg(lang))

    files = dir.entryList(QStringList() << QString("flacon_%1_??.qm").arg(lang), QDir.Files)
    if files.count():
        return dir.absolutePath() + "/" + files[0]

    return ""


#*******************************************************************
# This should keep reads and writes from getting EINTR.
#*******************************************************************
def setSigchldHandler():
    signal.signal(signal.SIGCHLD, handleSigchld)
    if hasattr(signal, 'siginterrupt'):
        signal.siginterrupt(signal.SIGCHLD, False)


#*******************************************************************
# This should keep reads and writes from getting EINTR.
#*******************************************************************
def handleSigchld(number, frame):
    # Apparently some UNIX systems automatically resent the SIGCHLD
    # handler to SIG_DFL.  Reset it just in case.
    setSigchldHandler()


#*******************************************************************
#
#*******************************************************************
def main(argv):
    setSigchldHandler()
    app = QtGui.QApplication(argv)


    qtTranslator = QTranslator(app)
    directory = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath)
    qtTranslator.load("qt_" + localeName(), directory)
    app.installTranslator(qtTranslator)

    qmFile = translationFileName()
    if qmFile:
        translator = QTranslator(app)
        translator.load(qmFile)
        app.installTranslator(translator)


    window = Flacon()
    sys.excepthook = window.exceptHook

    window.show()
    args = app.arguments()
    for i in range(1,  args.count()):
        window.addFileOrDir(args[i])

    sys.exit(app.exec_())


if __name__ == "__main__":
    # Workaround for Ubuntu bugs:
    # 11.10 bug - https://bugs.launchpad.net/ubuntu/+source/qt4-x11/+bug/805303
    # 12.10 bug - https://bugs.launchpad.net/ubuntu/+source/qt4-x11/+bug/1005677
    os.environ['LIBOVERLAY_SCROLLBAR'] = '0'

    main(sys.argv)
